/**
 * @widoc $.resizeListener
 * @namespace aux
 * @des 提供接口，可以对元素的尺寸变化绑定/解绑监听事件。
 * @type object
 * @demo resizeListener/demo 容器尺寸变化
 * @attr {function} fBind(el,fn) 对 el 添加 resize 监听
 * - el {object} 要监听的容器，jquery 对象
 * - fn {function} resize 时的回调方法 function()
 * @attr {function} fUnBind(el,fn) 对 el 移除 resize 监听
 * - el {object} 监听的容器，jquery 对象
 * - fn {function=} 要移除的监听事件 function()，默认移除全部事件
 * @attr {function} fResetSize(el) 手动触发尺寸重算，尺寸变化则触发回调
 * - el {object} 监听的容器，jquery 对象
 */
$.resizeListener = (function(){
    var api;
    // IE9 以下所有元素都支持 resize 事件，而其他多数浏览器中，只有窗体类的元素（window, frame）才支持 resize 事件
    var bSupportResize = navigator.appName == "Microsoft Internet Explorer" &&
        parseInt(navigator.appVersion.split(";")[1].replace(/[ ]/g, "").replace("MSIE","")) < 9;
    // resize, scroll 事件均会频繁触发，_fRaf 用于限制事件触发频率
    var _fRaf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
        function(fn){
            return window.setTimeout(fn, 20);
        };
    var _fCancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame ||
        window.clearTimeout;
    var fRafHandlerFac,// 用于生成 resize 事件触发时真正执行回调的方法（触发频率受 _fRaf 限制）
        fResetTriggers;// 不支持 resize 的浏览器中，重置 wi-resizelistener 内元素的尺寸位置
    // resize, scroll 事件触发时的处理函数
    var fOnResize = function(e){
        var dom = this,
            el = $(dom),
            oWiResizeData = el.data('wiResize');
        if(oWiResizeData){
            fResetTriggers && fResetTriggers(el);// 不支持 resize 的浏览器，需要在此重置 wi-resizelistener 内的元素尺寸位置
            if(oWiResizeData.fRaf){
                _fCancel(oWiResizeData.fRaf);
            }
            oWiResizeData.fRaf = _fRaf(fRafHandlerFac(el, dom, oWiResizeData));
        }
    };

    if(bSupportResize){
        fRafHandlerFac = function(el, dom, oWiResizeData){
            return function(){
                $.each(oWiResizeData.listeners, function(i, fn){
                    fn.call(dom);
                });
            };
        };
    }
    else{
        // 重置 resize 相关元素样式，根据当前状态调整，以便及时引起 scroll
        fResetTriggers = function(el){
            el.children('.wi-resizelistener').children().each(function(i, dom){
                var dChild;
                if(i == 0){
                    dChild = dom.firstElementChild;
                    dChild.style.width = dom.offsetWidth + 1 + 'px';
                    dChild.style.height = dom.offsetHeight + 1 + 'px';
                }
                dom.scrollLeft = dom.scrollWidth;
                dom.scrollTop = dom.scrollHeight;
            });
        };
        fRafHandlerFac = function(el, dom, oWiResizeData){
            return function(){
                var newW = el.innerWidth(),// el 当前尺寸
                    newH = el.innerHeight();
                if(newW != oWiResizeData.width || newH != oWiResizeData.height){
                    $.extend(oWiResizeData, {
                        width: newW,
                        height: newH
                    });
                    $.each(oWiResizeData.listeners, function(i, fn){
                        fn.call(dom);
                    });
                }
            };
        };
    }
    // 手动触发 resize 事件
    var fResetSize = function(el){
        if(el.data('wiResize')){
            fOnResize.call(el[0]);
        }
    };
    /**
     * 对指定元素添加 resize 监听
     * @param el {object} 要监听的容器，jquery 对象
     * @param fn {function} resize 时的回调方法
     */
    var fBind = function(el, fn){
        var oWiResizeData = el.data('wiResize'),// 记录 el 上是否已绑定监听
            aListeners;
        if(!fn){
            return;
        }
        if(oWiResizeData){// el 上已经绑定过 wiResize 监听，只需调整 listeners 监听事件数组
            aListeners = oWiResizeData.listeners;
            // fn 是已经绑定过的事件，不需重复绑定，直接返回；未绑定过的事件，加入 listeners 数组
            if(aListeners.indexOf(fn) == -1){
                aListeners.push(fn);
            }
        }
        else{// 未绑定过 wiResize 监听
            aListeners = [fn];
            if(bSupportResize){// IE9 以下
                el.data('wiResize', {
                    listeners: aListeners,
                    fRaf: null// 在 fOnResize() 触发时赋值
                });
                el.off('resize.wiResize')
                  .on('resize.wiResize', fOnResize)
            }
            else{
                if(el.css('position') == 'static'){
                    el.css('position', 'relative');// 设置为定位元素
                }
                el.append('<div class="wi-resizelistener">' +
                    '<div class="wi-resizelistener-in1"><div></div></div>' +
                    '<div class="wi-resizelistener-in2"></div>' +
                    '</div>');
                el.data('wiResize', {
                    width: -1,
                    height: -1,
                    listeners: aListeners,
                    fRaf: null// 在 fOnResize() 触发时赋值
                });
                // scroll 不支持冒泡，必须在捕获阶段绑定事件 - fResetTriggers 中的 dom 操作会在事件绑定完成后渲染，因此可能会导致初始化时默认执行一次回调
                el.get(0).addEventListener('scroll', fOnResize, true);
            }
            fResetSize(el);// 第一次绑定 wiResize 监听时，强制调用（不支持 resize 时必须，通过 fResetSize 调用 fResetTriggers 设置 wi-resizelistener 内元素的尺寸位置；支持时，调用此方法，仅为使不同浏览器效果一致）
        }
    };
    /**
     * 对指定元素移除 resize 监听
     * @param el {object} 监听的容器，jquery 对象
     * @param fn {function=} 要移除的监听事件，默认移除全部事件
     */
    var fUnbind = function(el, fn){
        var oWiResizeData = el.data('wiResize');
        var aListener, fnIdx;
        if(oWiResizeData){
            aListener = oWiResizeData.listeners;
            if(fn){
                fnIdx = aListener.indexOf(fn);
                if(fnIdx == -1){
                    return;
                }
                else{
                    aListener.splice(fnIdx, 1);
                }
            }
            else{// 全部移除
                aListener.length = 0;
            }

            if(aListener.length == 0){// 所有事件都被移除，销毁监听
                if(bSupportResize){// IE11 以下
                    el.off('resize.wiResize');
                }
                else{
                    el.get(0).removeEventListener('scroll', fOnResize, true);
                    el.children('.wi-resizelistener').remove();
                    _fCancel(oWiResizeData.fRaf);
                }
                el.removeData('wiResize');
            }
        }
    };
    api = {
        fBind: fBind,
        fUnBind: fUnbind,
        fResetSize: fResetSize// 初始化时可能由于元素隐藏或祖先元素隐藏，导致 .wi-resizelistener 的元素尺寸位置异常，此时允许用户手动调用此方法触发 resize 回调
    };
    return api;
})();